import 'dart:convert';
import 'dart:io';

import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_fit/app/controllers/base/base_controller.dart';
import 'package:share_fit/app/controllers/device/dail/dial_model.dart';
import 'package:share_fit/app/helper/ext/tr_ext.dart';
import 'package:share_fit/app/tools/app_util.dart';
import 'package:share_fit/app/tools/locale_tool.dart';
import 'package:share_fit/app/tools/log_utils.dart';
import 'package:share_fit/app/ute/uteped_const.dart';
import 'package:share_fit/app/ute/uteped_device.dart';
import 'package:share_fit/generated/locales.g.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
import 'package:intl/intl.dart';

import 'dial_const.dart';
import 'dial_state.dart';

class DialLogic extends BaseController {
  final String methodGetWatchFaceOnlineInformation = 'getWatchFaceOnlineInformation';
  final String methodUploadWatchFace = 'uploadWatchFace';
  final String methodDownload = 'downloadFile';
  final String methodApplyWatchFace = 'applyWatchFace';
  final String methodGetWatchFaceParams = 'getWatchFaceParams';
  final String methodGetWatchFaceInfo = 'getWatchFaceInfo';
  final String methodSetImageWatchFace = 'setImageWatchFace';
  final String methodGetImageWatchFace = 'getImageWatchFace';
  final String methodUploadImageWatchFace = 'uploadImageWatchFace';
  final String methodDownloadImageWatchFace = 'downloadImageWatchFace';
  final String methodGetServerWatchFace = 'getServerWatchFace';
  final DialState state = DialState();


  @override
  void onInit() {
    super.onInit();
    WakelockPlus.enable();
  }

  @override
  void onClose() {
    WakelockPlus.disable();
    super.onClose();
  }

  void updateDialImage(value) {
    state.dialImagePath = value;
    update(['update_dial_image']);
  }

  void updateImageEditState(value) {
    state.imageEdit = value;
    update(['update_dial_image_edit']);
  }

  void updatePositionIndex(value) {
    state.positionIndex = value;
    update(['position_index']);
  }

  void updateLimitSelected(value) {
    state.limitSelectedIndex = value;
    update(['update_limit_selected']);
  }

  void updateDownloadDials() {
    update([DialConst.dialDownloadViewId]);
  }

  void updateDials(List<DialModel> value, isMore) {
    if(!isMore) {
      state.lists.clear();
    }
    state.lists.addAll(value);
    update([DialConst.dialListViewId]);
  }

  void isShowSelected(value) {
    state.isShowSelected = value;
    state.selectedAll = false;
    update([DialConst.showSelectedViewId, DialConst.dialDownloadViewId]);
  }

  Future<List<DialModel>> getWatchFaceOnlineInformation(isMore) async {
    logger.d('getWatchFaceOnlineInformation@@@  =====>>> : start');

    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      AppUtil.showDeviceDisconnectTips();
      return [];
    }

    await getWatchFaceParams();

    String params = '{"languageType":"${LocaleTool.locale.split('_')[0]}", "refresh":"${!isMore}", "height":${state.height}, "width":${state.width},"maxCapacity":${state.maxCapacity},"shape":${state.shape},"screenCorner":${state.cornerRadius}}';
    logger.d('state.watchFaceParams : $params');
    var info = await dRemoteReqAsync(methodGetWatchFaceOnlineInformation, {UTEPedConst.keyValue: params});
    if(info is bool) {
      return [];
    }

    Map map = jsonDecode(info);

    if(map['flag']<=0) {
      return [];
    }

    state.flag = true;
    state.totalCount = map['totalCount'];
    var listDynamic = map['watchFaceOnlineOneInfoList'];
    List<Map<String, dynamic>> listMap = List<Map<String, dynamic>>.from(listDynamic);

    List<DialModel> lists = [];
    for(var element in listMap) {
      lists.add(DialModel().fromJson(element));
    }
    updateDials(lists, isMore);
    return lists;
  }

  void updateUploadFileValue(value) {
    state.uploadFileValue = value;
    updateUploadFileText('${LocaleKeys.dial_sync_uploading.ts} ${(value*100).toInt()}%');
    update([DialConst.dialUploadFileViewId]);
  }

  void updateUploadFileText(value) {
    state.uploadFileText = value;
    update([DialConst.dialUploadFileTextViewId]);
  }

  Future<void> uploadWatchFace(String resource) async {
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      AppUtil.showDeviceDisconnectTips();
      return;
    }

    updateUploadFileText(LocaleKeys.dial_data_ready.ts);
    var dir = await getApplicationDocumentsDirectory();
    Map map = {'url_path':resource, 'path':dir.path};
    var info = await dRemoteReqAsync(methodUploadWatchFace, {UTEPedConst.keyValue:map});
    logger.d('uploadWatchFace###  =====>>> : $info');
  }

  Future<void> download(String resource) async {
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }

    updateUploadFileText(LocaleKeys.dial_downloading.ts);
    var dir = await getApplicationDocumentsDirectory();
    Map map = {'url_path':resource, 'path':dir.path};
    var info = await dRemoteReqAsync(methodDownload, {UTEPedConst.keyValue:map});
    logger.d('download###  =====>>> : $info');
  }

  Future<void> applyWatchFace(String id, String version, int operate) async {
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }

    logger.d('applyWatchFace@@@  =====>>> : start');
    Map map = {"id":id, "version":version, "operate":operate};
    Map<String, Object> params = {UTEPedConst.keyValue: map};
    var info = await dRemoteReqAsync(methodApplyWatchFace, params);
    logger.d('applyWatchFace###  =====>>> : $info');
  }

  Future<void> getWatchFaceParams() async {
    logger.d('getWatchFaceParams@@@  =====>>> : start');
    var info = await dRemoteReqAsync(methodGetWatchFaceParams, {});
    if(info is bool && !info) {
      return;
    }
    logger.d('getWatchFaceParams ==>> $info');
    state.watchFaceParams = jsonDecode(info);

    //{"earlyWatchFaceVersion":"1.0.0","height":466,"isSupportSort":false,"maxFileSize":409600,"maxWatchFaceVersion":"1.0.0","screenCorner":0,"screenType":0,"supportFileType":0,"watchFaceCount":1,"watchFaceExtinguishCount":0,"width":466}

    var data = state.watchFaceParams[UTEPedConst.keyData];
    logger.d('getWatchFaceParams ==>> ${data['height']}');
    state.height = data['height'];
    state.width = data['width'];
    state.maxCapacity = data['maxFileSize'];
    state.shape = data['screenType'];
    state.cornerRadius = data['screenCorner'];

    state.watchFaceCount = data['watchFaceCount'];
    logger.d('getWatchFaceParams###  =====>>> : ${state.watchFaceParams}/${state.watchFaceCount}/');
  }

  Future<void> getWatchFaceInfo() async {
    logger.d('getWatchFaceInfo@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }
    var info = await dRemoteReqAsync(methodGetWatchFaceInfo, {});
    if(info is bool && !info) {
      return;
    }

    logger.d('getWatchFaceInfo ====>> $info');
    Map<dynamic, dynamic> map = jsonDecode(info);

    if(map.containsKey(UTEPedConst.keySuccess) && map[UTEPedConst.keySuccess]) {
      state.watchFaceInfo.clear();
      state.watchFaceInfo.addAll(map[UTEPedConst.keyData]['workFaceList']);
    }
  }

  Future<void> getImageWatchFace() async {
    logger.d('getImageWatchFace@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }
    var info = await dRemoteReqAsync(methodGetImageWatchFace, {});
    if(info is bool && !info) {
      return;
    }
    logger.d('getImageWatchFace ====>> $info');
    state.imageWatchFace = jsonDecode(info);
    if(state.imageWatchFace.isEmpty) {
      return;
    }

    var data = state.imageWatchFace[UTEPedConst.keyData];
    state.maxCount = data['maxCount'];
    if(data['imageInfoList']!=null) {
      List imageInfoList = data['imageInfoList'];
      if(imageInfoList.isNotEmpty) {
        state.imageInfoList = imageInfoList[0];
        downloadImageWatchFace(state.imageInfoList['name']??'', state.imageInfoList['index']??0);
      }
    }

    logger.d('getImageWatchFace@@@  =====>>> : $info/${state.imageInfoList}/${data['positionIndex']}');
    updatePositionIndex(data['positionIndex']);
  }

  Future<void> setImageWatchFace(int positionIndex) async {
    logger.d('setImageWatchFace@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }

    // if(state.imageInfoList.isEmpty) {
    //   return;
    // }

    Map map = {'name':state.imageInfoList['name']??'', 'index':'${state.imageInfoList['index']??0}', 'positionIndex':'$positionIndex', };
    var info = await dRemoteReqAsync(methodSetImageWatchFace, {UTEPedConst.keyValue:map});
  }

  Future<void> uploadImageWatchFace() async {
    logger.d('uploadImageWatchFace@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }
    if(state.dialImagePath.isEmpty || (state.maxWidth==0 && state.maxHeight==0)) {
      return;
    }
    state.updatePosition = true;
    // var dir = await getApplicationDocumentsDirectory();
    // Map map = {'index':'0', 'path':'${dir.path}/111222.png'};
    Map map = {'index':'0', 'path':state.dialImagePath, 'width':'${state.maxWidth.toInt()}', 'height':'${state.maxHeight.toInt()}'};

    var info = await dRemoteReqAsync(methodUploadImageWatchFace, {UTEPedConst.keyValue:map});
    logger.d('uploadImageWatchFace@@@  =====>>> : $info/');
    if(info) {
      save('dial_image_watchface_path', state.dialImagePath);
    }
    else {
      updateUploadFileValue(0.0);
      updateUploadFileText('');
    }
  }

  Future<void> downloadImageWatchFace(String name, int positionIndex) async {
    logger.d('downloadImageWatchFace@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      return;
    }
    String path = read('dial_image_wacthface_path', defaultValue: '');
    if(path.isNotEmpty) {
      File file = File(path);
      if(file.existsSync()) {
        updateDialImage(path);
        return;
      }
    }

    var dir = await getApplicationDocumentsDirectory();
    String fileName = name.isEmpty?'image_temp.png':name;
    Map map = {'name':fileName, 'index':'$positionIndex', 'path':dir.path};

    var info = await dRemoteReqAsync(methodDownloadImageWatchFace, {UTEPedConst.keyValue:map});
    if(info && state.dialImagePath.isEmpty) {
      updateDialImage('${dir.path}/$fileName');
    }
    logger.d('downloadImageWatchFace@@@  =====>>> : $info// ${dir.path}/$fileName');
  }

  Future<void> getServerWatchFace() async {
    logger.d('getServerWatchFace@@@  =====>>> : start');
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if(!isConnected) {
      AppUtil.showDeviceDisconnectTips();
      return;
    }
    var info = await dRemoteReqAsync(methodGetServerWatchFace, {});
    logger.d('getServerWatchFace ====>> $info/');
    if(info is bool && !info) {
      return;
    }
    state.serverWatchFace = jsonDecode(info);
    logger.d('getServerWatchFace ====>> ${state.serverWatchFace.isEmpty}');
    if(state.serverWatchFace.isEmpty) {
      return;
    }
    state.serverWatchFaceInfo = jsonDecode(jsonEncode(state.serverWatchFace[0]));
    state.serverWatchFacePosition = jsonDecode(jsonEncode(state.serverWatchFaceInfo['position']));
    String dpi = state.serverWatchFaceInfo['dpi'];
    state.maxWidth = double.parse(dpi.split('*')[0]);
    state.maxHeight = double.parse(dpi.split('*')[1]);
    state.positionIndex = state.serverWatchFaceInfo['defaultWidgetPosition']+1;
    state.widgetPosition1Bg = state.serverWatchFacePosition[0]['defaultWidget'];
    state.widgetPosition2Bg = state.serverWatchFacePosition[1]['defaultWidget'];

    logger.d('getServerWatchFace@@@  =====>>> : $info/${state.positionIndex}');

    update(['custom_watchface_tab']);
  }


  Future<void> getImage() async {
    logger.d('${state.maxWidth}/${state.maxHeight}/${state.maxWidth.toInt()}/${state.maxHeight.toInt()}/');
    final XFile? file = await ImagePicker().pickImage(
        source: ImageSource.gallery,
        maxWidth: state.maxWidth,
        maxHeight: state.maxHeight);
    if (file != null) {
      _processUploadImage(file);
    }
  }

  Future<void> getPhoto() async {
    final XFile? file = await ImagePicker().pickImage(
        source: ImageSource.camera,
        maxWidth: state.maxWidth,
        maxHeight: state.maxHeight);
    if (file != null) {
      logger.d('getPhoto file: ${file.path}');
      _processUploadImage(file);
    }
  }

  Future<void> _processUploadImage(XFile file) async {
    if (state.dialImagePath.isNotEmpty) {
      var prevFile = File(state.dialImagePath);
      if (prevFile.existsSync()) {
        prevFile.deleteSync();
      }
    }

    CroppedFile info = await cropImage(image: file.path,
        width: state.maxWidth.toInt(),
        height: state.maxHeight.toInt());
    logger.d('cropImage: ${info.path}');

    var imgFile = File(file.path);
    imgFile.deleteSync();

    var dir = await getApplicationDocumentsDirectory();
    final String fileName = DateFormat('yyyyMMddHHmmss').format(DateTime.now());
    var newPath = "${dir.path}/$fileName.png";
    var renameFile = File(info.path);
    renameFile.renameSync(newPath);

    updateDialImage(newPath);


  }

  cropImage({required image,
    required width,
    required height,
    aspectRatio,
    androidUiSettings,
    iOSUiSettings}) async {

    ///直接图片压缩的库 flutter_image_compress: ^2.1.0

    logger.d('w:$width/h:$height');
    String imagePth = "";
    if (image is String) {
      imagePth = image;
    } else if (image is File) {
      imagePth = image.path;
    } else {
      throw ("文件路径错误");
    }
    CroppedFile? croppedFile = await ImageCropper().cropImage(
        sourcePath: imagePth,
        maxWidth: width,
        maxHeight: height,
        compressFormat: ImageCompressFormat.png,
        compressQuality: 90,
        uiSettings: [
          AndroidUiSettings(
              toolbarTitle: '',
              initAspectRatio: CropAspectRatioPreset.original,
              hideBottomControls: true,
              lockAspectRatio: true),
          IOSUiSettings(
            title: '',
            resetAspectRatioEnabled: false,
            aspectRatioPickerButtonHidden: true,
            resetButtonHidden: true,
            rotateButtonsHidden: true,
          )
        ]
    );
    logger.d('cropImage: ${croppedFile!.path}');
    return croppedFile;
  }

  @override
  String getRemotePath() {
    return UTEPedConst.pathWatchFace;
  }

  @override
  void initState() {
    // getWatchFaceParams();
    getWatchFaceInfo().then((value) => clean());
    getImageWatchFace();
    // getServerWatchFace();
    // getWatchFaceOnlineInformation(false);
  }

  @override
  void onRemoteEvent(event, method) {
    logger.d('onRemoteEvent:$event/$method');
    if(method==methodUploadWatchFace || method == methodUploadImageWatchFace || method == methodDownloadImageWatchFace) {
      var action = event[UTEPedConst.keyAction];
      if (action == UTEPedConst.actionProgress) {
        int progress = event[UTEPedConst.keyProgress];
        int duration = event[UTEPedConst.keyDuration];
        updateUploadFileValue(progress/duration);
      }
      else if(action == UTEPedConst.actionComplete) {
        state.isUploadFile = false;
        Future.delayed(const Duration(seconds: 1), (){
          updateUploadFileText(LocaleKeys.dial_sync_completed.ts);
          // if(state.updatePosition) {
          //   state.updatePosition = false;
          //   setImageWatchFace(state.positionIndex);
          // }
        });
        Future.delayed(const Duration(seconds: 3), (){
          updateUploadFileValue(0.0);
          updateUploadFileText('');
        });
      }
      else if (action == UTEPedConst.actionFailed) {
        updateUploadFileValue(0.0);
        updateUploadFileText('');
        _showFailedTips();
      }
    }

    if(method==methodDownloadImageWatchFace) {
      // int progress = event['progress'];
      // int duration = event['duration'];
      // updateUploadFileValue(progress/duration);
    }

    if(method==methodSetImageWatchFace) {

    }
  }

  Future<void> _showFailedTips() async {
    bool isConnected = await UTEPedDevice.instance.isConnected();
    if (isConnected) {
      showTips(LocaleKeys.info.ts, LocaleKeys.dial_sync_failed.ts);
    }
    else {
      showTips(LocaleKeys.info.ts, LocaleKeys.device_not_connected.ts);
    }
  }

  /// 未知情况下，本地保存的数据丢失了，直接先清除手表上表盘
  void clean() {
    List<dynamic> limitId = [];
    for(var info in state.watchFaceInfo) {
      Map<dynamic, dynamic> map = jsonDecode(jsonEncode(info));
      if((4&map['type'])==4){
        limitId.add(map);
      }
    }
    logger.d('clean limitIds:$limitId');
    for(var info in limitId) {
      bool hasData = false;
      for(var element in state.downlodLists) {
        Map temp = jsonDecode(element);
        if(info['id']=='${temp['bleId']}') {
          hasData = true;
          break;
        }
      }
      logger.d('clean data: $hasData/$limitId/');
      if(!hasData) {
        applyWatchFace('${info['id']}', '${info['version']}', 2);
      }
    }

  }
}
